﻿// <copyright file="DiffuseMaterial.cs" company="Axelerate Solutions S.A.">
// Copyright (c) Axelerate Solutions S.A.  All rights reserved.
// </copyright>

namespace Axelerate.Silverlight3D.Media.Media3D
{
    using System;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Ink;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;
    using System.Windows.Media.Imaging;
    using Axelerate.Silverlight3D.Controls;

    /// <summary>
    /// File name: DiffuseMaterial.cs
    /// Company: Axelerate Solutions S.A.
    /// Created By: Daniel Rojas
    /// Created Date: 10/07/2008 13:38:00 h.
    /// Description: Allows the application of a 2-D brush, like a SolidColorBrush to a diffusely-lit 3-D model.
    ///   <Modifications>
    ///   </Modifications>
    /// </summary>
    public class DiffuseMaterial : Material
    {
        #region Fields
        /// <summary>
        /// Identifies the AmbientColor dependency property.
        /// </summary>
        public static readonly DependencyProperty AmbientColorProperty =
                    DependencyProperty.Register(
                          "AmbientColor",
                          typeof(Color),
                          typeof(DiffuseMaterial),
                          new PropertyMetadata(OnAmbientColorPropertyChanged));

        /// <summary>
        /// Identifies the Brush dependency property.
        /// </summary>
        public static readonly DependencyProperty BrushProperty =
                    DependencyProperty.Register(
                          "Brush",
                          typeof(Brush),
                          typeof(DiffuseMaterial),
                          new PropertyMetadata(OnBrushPropertyChanged));

        /// <summary>
        /// Identifies the Color dependency property.
        /// </summary>
        public static readonly DependencyProperty ColorProperty =
                    DependencyProperty.Register(
                          "Color",
                          typeof(Color),
                          typeof(DiffuseMaterial),
                          new PropertyMetadata(OnColorPropertyChanged));

     
        // Future use.
        ////private Matrix3D uvToTexture;
        ////private Matrix3D textureInverse;
        ////private MatrixTransform brushTransform = new MatrixTransform();
        
        #endregion
        
        #region Constructors
        public DiffuseMaterial() : base()
        {
            this.Brush = new SolidColorBrush(Colors.Gray);
            this.Color = Colors.White;
            this.AmbientColor = Colors.White;
        }

        public DiffuseMaterial(Brush brush)
            : base()
        {
            this.Brush = brush;
            this.Color = Colors.White;
            this.AmbientColor = Colors.White;
        }
        
        #endregion

        #region Properties
        /// <summary>
        /// Gets or sets a color that represents the ambient color emitted by the Material. This is a dependency property.
        /// </summary>
        public Color AmbientColor
        {
            get
            {

                return (Color)this.GetValue(AmbientColorProperty);
            }
            set
            {
                this.SetValue(AmbientColorProperty, value);
            }
        }

        /// <summary>
        /// Brush to be applied as a Material to a 3-D model. This is a dependency property.
        /// </summary>
        public Brush Brush
        {
            get
            {

                return (Brush)this.GetValue(BrushProperty);
            }
            set
            {
                this.SetValue(BrushProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets the color allowed to emit from the Material. This is a dependency property.
        /// </summary>
        public Color Color
        {
            get
            {

                return (Color)this.GetValue(ColorProperty);
            }
            set
            {
                this.SetValue(ColorProperty, value);
            }
        }
        #endregion

        #region Methods
        /// <summary>
        /// Raise the AmbeintColor changed event
        /// </summary>
        /// <param name="d">DependencyObject owner of the property</param>
        /// <param name="e">DependencyPropertyChangedEventArgs with the necesary information</param>
        private static void OnAmbientColorPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((DiffuseMaterial)d).NotifyPropertyChanged("AmbientColor");
        }
        
        /// <summary>
        /// Raise the Brush changed event
        /// </summary>
        /// <param name="d">DependencyObject owner of the property</param>
        /// <param name="e">DependencyPropertyChangedEventArgs with the necesary information</param>
        private static void OnBrushPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((DiffuseMaterial)d).NotifyPropertyChanged("Brush");
        }
        
        /// <summary>
        /// Raise the Color changed event
        /// </summary>
        /// <param name="d">DependencyObject owner of the property</param>
        /// <param name="e">DependencyPropertyChangedEventArgs with the necesary information</param>
        private static void OnColorPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((DiffuseMaterial)d).NotifyPropertyChanged("Color");
        }

        internal override Material Clone()
        {
            if (this.Brush == null)
            {
                return new DiffuseMaterial();
            }
            else
            {
                
                return new DiffuseMaterial(this.Brush);
            }
        }

        #endregion

        #region IFormattable Members

        public override string ToString(string format, IFormatProvider formatProvider)
        {
            throw new NotImplementedException();
        }

        #endregion

       

        internal override Brush GetBrush(System.Collections.Generic.List<Light> Lights, Vector3D SurfaceNormal, Vector3D CameraViewDir, bool IsSpecular)
        {
            System.Windows.Media.Brush result;
            result = this.Brush;
            if (Brush != null)
            {
                // if not have lights use LightModel == none
                if (Lights.Count == 0)
                {
                    return Brush;
                }
                
                // color components
                double ar, ag, ab;

                // color components
                double br, bg, bb;
                
                // color components
                double lr, lg, lb;
               
                // color components
                double r, g, b; 
                
                // Calculate material participation ambient color
                ar = (double)this.AmbientColor.R / 255.0f;
                ag = (double)this.AmbientColor.G / 255.0f;
                ab = (double)this.AmbientColor.B / 255.0f;

                // Base color contribution
                br = (double)this.Color.R / 255.0f;
                bg = (double)this.Color.G / 255.0f;
                bb = (double)this.Color.B / 255.0f;

                r = 0;
                g = 0;
                b = 0;
                
                // Mod by lights
                foreach (Light item in Lights)
                {
                    Point3D lightcolorIntensity = item.GetColorIntencityByNormal(SurfaceNormal);
                    // Get light color contribution
                    lr = lightcolorIntensity.X;
                    lg = lightcolorIntensity.Y;
                    lb = lightcolorIntensity.Z;

                    if (item is AmbientLight)
                    {
                        r += ar * lr;
                        g += ag * lg;
                        b += ab * lb;
                    }   
                    else
                    {
                        // Append to final color contributions
                        r += br * lr;
                        g += bg * lg;
                        b += bb * lb;
                    }
                    if (r > 1) r = 1;
                    if (g > 1) g = 1;
                    if (b > 1) b = 1;
                }

                if (this.Brush is SolidColorBrush)
                {
                    System.Windows.Media.Color newCol;
                    System.Windows.Media.Color oldCol = ((SolidColorBrush)this.Brush).Color;
                    newCol = System.Windows.Media.Color.FromArgb(oldCol.A, (byte)(oldCol.R * r), (byte)(oldCol.G * g), (byte)(oldCol.B * b));
                    result = new SolidColorBrush(newCol);
                    result.Opacity = this.Brush.Opacity;
                }
                else if (this.Brush is GradientBrush)
                {
                    GradientBrush obrush = this.Brush as GradientBrush;
                    GradientBrush gresult = null;
                    if (this.Brush is LinearGradientBrush)
                    {
                        gresult = new LinearGradientBrush();
                    }
                    else if (this.Brush is RadialGradientBrush)
                    {
                        gresult = new RadialGradientBrush();
                    }
                    gresult.ColorInterpolationMode = obrush.ColorInterpolationMode;
                    gresult.MappingMode = obrush.MappingMode;
                    gresult.Opacity = obrush.Opacity;
                    gresult.RelativeTransform = obrush.RelativeTransform;
                    gresult.SpreadMethod = obrush.SpreadMethod;
                    gresult.Transform = obrush.Transform;
                    foreach (GradientStop stop in obrush.GradientStops)
                    {
                        GradientStop newstop = new GradientStop();
                        newstop.Offset = stop.Offset;

                        System.Windows.Media.Color newCol;
                        newCol = System.Windows.Media.Color.FromArgb(stop.Color.A, (byte)(stop.Color.R * r), (byte)(stop.Color.G * g), (byte)(stop.Color.B * b));

                        newstop.Color = newCol;

                        gresult.GradientStops.Add(newstop);
                    }
                }

            }
                        
            return result;
        }
    }
}
